﻿#pragma once

#include "Core/EventDispatcher.h"
#include "Render/FrameBufferStack.h"

class CORE_API Window {
public:
  Window(int width, int height, const std::string &title);
  ~Window();

  static Window &GetInstance();

  void SetWidth(int width) { m_width = width; }
  void SetHeight(int height) { m_height = height; }
  inline int32_t GetWidth() const { return m_width; }
  inline int32_t GetHeight() const { return m_height; }

  void SetClearColor(const glm::vec4 &color) { m_clearColor = color; }

  inline Ref<FrameBufferStack> GetFrameBufferStack()  { return m_frameBufferStack; }

  void InitWindow();
  void SetCallbacks() const;

  void BeginFrame();
  void ResetFrame();
  void FinalizeFrame();
  bool ShouldClose() const;
  void SwapBuffers();
  void PollEvents();
  bool ShouldRender() const;

  GLFWwindow *GetNativeWindow() const { return m_window; }
  float GetAspectRatio() const;

  EventDispatcher &GetDispatcher() { return m_dispatcher; }

  void SetEnableCursor(bool enable) const;

  static void FramebufferResizeCallback(GLFWwindow *window, int width,int height);
  static void MouseButtonCallback(GLFWwindow *window, int button, int action,int mods);
  static void CursorPosCallback(GLFWwindow *window, double xPos, double yPos);
  static void ScrollCallback(GLFWwindow *window, double xOffset,double yOffset);
  static void KeyCallback(GLFWwindow *window, int key, int scancode, int action,int mods);                  

  static void OnWindowError(int32_t errorCode, const char *description);
  static void OnOpenGlMessage(GLenum source,
                              GLenum type,
                              GLuint id,
                              GLenum severity,
                              GLsizei length,
                              const GLchar *message,
                              const void *userParam);

private:
  GLFWwindow *m_window = nullptr;

  EventDispatcher m_dispatcher;

  int m_width = 0;
  int m_height = 0;
  std::string m_title;
  float m_aspectRatio = 1.0f;

  glm::vec4 m_clearColor = {0, 0, 0, 1};

  Ref<FrameBuffer> m_frameBuffer{nullptr};
  Ref<FrameBufferStack> m_frameBufferStack = CreateRef<FrameBufferStack>();
};